﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Dynamic;
using System.Linq;
using System.Reflection;
using System.Web.Mvc;
using Se.Framework.Common;
using Se.Framework.Common.Config.Providers;
using Se.Framework.Common.Data;
using Se.Framework.Common.Extentions;
using Se.Framework.Web.Mvc.GenericCrud.Attributes;
using Trirand.Web.Mvc;

namespace Se.Framework.Web.Mvc.GenericCrud
{
    public abstract class GenericCrudController<T> : Controller
        where T : GenericCrudViewModel, new()
    {
        private static readonly char[] idSplitCharArray = new char[] { ',' };

        protected virtual string EntityName { get; private set; }

        public virtual ActionResult Entity(string entity)
        {
            EntityName = entity;
            var builder = GridBuilderFactory.Factory.FromType(TypeProviderManager.Default.GetTypeByClassName(EntityName));
            var model = new T
            {
                Grid = builder.Build(Url),
                Title = builder.TableProperties.Name
            };

            if (Request.IsAjaxRequest())
            {
                return PartialView(model);
            }
            else
            {
                return View();
            }
        }

        [QueryStringConvertionFilterAttribute]
        public virtual JsonResult GetData(string entity)
        {
            EntityName = entity;
            var builder = GridBuilderFactory.Factory.FromType(TypeProviderManager.Default.GetTypeByClassName(EntityName));

            JQGrid grid = builder.Build(this.Url);

            IBusinessLayer<IGenericEntity> repository = GetBusinessLayer(builder.TableProperties.EntityType);

            return grid.DataBind(repository.GetItems());
        }

        [HttpPost]
        public JsonResult PostData(string entity)
        {
            EntityName = entity;
            var builder = GridBuilderFactory.Factory.FromType(TypeProviderManager.Default.GetTypeByClassName(EntityName));
            JQGrid grid = builder.Build(Url);
            dynamic result = new ExpandoObject();
            try
            {
                result.Valid = true;
                IBusinessLayer<IGenericEntity> repository = GetBusinessLayer(builder.TableProperties.EntityType);
                switch (grid.AjaxCallBackMode)
                {
                    case AjaxCallBackMode.EditRow:
                        IGenericEntity updateItem = repository.Find(GetCurrentIds(builder.TableProperties.KeyFieldName).First());
                        Populate(updateItem, Request.Form, builder.TableProperties);
                        repository.UpdateItem(updateItem);
                        break;
                    case AjaxCallBackMode.AddRow:
                        IGenericEntity newItem = (IGenericEntity)Activator.CreateInstance(builder.TableProperties.EntityType);
                        Populate(newItem, Request.Form, builder.TableProperties);
                        repository.AddItem(newItem);
                        break;
                    case AjaxCallBackMode.DeleteRow:
                        foreach (var deleteId in GetCurrentIds(builder.TableProperties.KeyFieldName))
                        {
                            IGenericEntity deleteItem = repository.Find(deleteId);
                            repository.DeleteItem(deleteItem);
                        }
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }

                if (result.Valid)
                {
                    ObjectRegistry.Current.Resolve<IUnitOfWork>().Commit();
                }
            }
            catch (Exception ex)
            {
                result.Valid = false;
                result.Message = ex.Message;
                result.Exception = ex.ToString();
            }

            return Json(result);
        }

        private IEnumerable<int> GetCurrentIds(string idFieldName)
        {
            return Request[idFieldName].Split(idSplitCharArray, StringSplitOptions.RemoveEmptyEntries).Select(s => int.Parse(s));
        }

        private static void Populate(object entity, NameValueCollection values, IGenericCrudTableData tableData)
        {
            foreach (var key in values.AllKeys.Where(p => p != tableData.KeyFieldName))
            {
                var property = tableData.EntityType.GetProperty(key);
                if (property == null)
                {
                    continue;
                }
                if (!property.TrySetValueFromString(values[key], entity))
                {
                    if (typeof(IGenericEntity).IsAssignableFrom(property.PropertyType))
                    {
                        var repo = (IBusinessLayer<IGenericEntity>)ObjectRegistry.Current.Resolve(typeof(IBusinessLayer<>).MakeGenericType(property.PropertyType));
                        property.SetValue(entity, repo.Find(int.Parse(values[key])), null);
                    }
                }
            }
        }

        internal static IBusinessLayer<IGenericEntity> GetBusinessLayer(Type type)
        {
            return (IBusinessLayer<IGenericEntity>)Activator.CreateInstance(typeof(GenericEntityConverterBusinessLayer<>).MakeGenericType(type));
        }
    }
}